package koala.kuangjiademo.util;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.net.Uri;
import android.os.Build;
import android.os.Handler;
import android.support.annotation.IdRes;
import android.support.annotation.LayoutRes;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentTabHost;
import android.support.v4.content.FileProvider;
import android.util.Base64;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.view.Window;
import android.view.WindowManager;
import android.webkit.MimeTypeMap;
import android.widget.BaseAdapter;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.PopupWindow;
import android.widget.RadioGroup;
import android.widget.ScrollView;
import android.widget.TabHost;
import android.widget.TextView;
import android.widget.Toast;

import com.android.internal.util.Predicate;
import com.dhy.xintent.XCommon;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

import java.io.File;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import koala.kuangjiademo.R;
import koala.kuangjiademo.data.Callback;

public class Helper {
    /**
     * use tabs(tabsId) child as tab, and add new FragmentTabHost to it.<br/>
     * You can PUT tabs bottom or top of your layout, set background.
     *
     * @param fragments    Map(tabId, fragmentClass)
     * @param tabsId       the tabs layout id, PUT it anywhere you want
     * @param tabContentId the tabContent layout id, PUT it anywhere you want
     */
    public static FragmentTabHost initFragmentTabHost(FragmentActivity activity, Map<Integer, Class> fragments, @IdRes int tabsId, @IdRes int tabContentId) {
        ViewGroup tabs = (ViewGroup) activity.findViewById(tabsId);
        FragmentTabHost tabHost = new FragmentTabHost(activity);
        tabs.addView(tabHost, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
        tabHost.setup(activity, activity.getSupportFragmentManager(), tabContentId);
        tabHost.getTabWidget().setDividerDrawable(null);
        for (int i = 0; i < fragments.size(); i++) {
            View view = tabs.getChildAt(0);
            tabs.removeView(view);
            Class fragment = fragments.get(view.getId());
            TabHost.TabSpec tabSpec = tabHost.newTabSpec(fragment.getName()).setIndicator(view);
            tabHost.addTab(tabSpec, fragment, null);
        }
        fragments.clear();
        tabHost.findViewById(tabContentId).setId(0);//unuse FragmentTabHost's tabContent, use the outer instead
        return tabHost;
    }

    public static void initGroupAsListView(ViewGroup group, BaseAdapter adapter) {
        int size = adapter.getCount();
        while (group.getChildCount() > size) {
            group.removeViewAt(size);
        }
        for (int i = 0; i < size; i++) {
            View convertView = group.getChildAt(i);
            if (convertView != null) {
                adapter.getView(i, convertView, group);
            } else {
                convertView = adapter.getView(i, null, group);
                group.addView(convertView, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
            }
        }
    }

    public static void setOnClickListenerForClickableChild(ViewGroup root, View.OnClickListener listener) {
        List<View> views = getViews(root, new Predicate<View>() {
            @Override
            public boolean apply(View view) {
                return view.isClickable();
            }
        });
        for (View view : views) {
            view.setOnClickListener(listener);
        }
    }

    public static void setOnClickListenerForChild(ViewGroup root, View.OnClickListener listener, Predicate<View> predicate) {
        List<View> views = getViews(root, predicate);
        for (View view : views) {
            view.setOnClickListener(listener);
        }
    }

    public static List<View> getViewsByTag(ViewGroup root, final String tag) {
        return getViews(root, new Predicate<View>() {
            @Override
            public boolean apply(View view) {
                return tag.equals(view.getTag());
            }
        });
    }

    public static List<View> getViews(ViewGroup root, Predicate<View> predicate) {
        List<View> views = new ArrayList<>();
        for (int i = 0; i < root.getChildCount(); i++) {
            final View child = root.getChildAt(i);
            if (child instanceof ViewGroup) {
                views.addAll(getViews((ViewGroup) child, predicate));
            }
            if (predicate.apply(child)) {
                views.add(child);
            }
        }
        return views;
    }

    public static Dialog showDialogForActionDone(final Context context, boolean finish) {
        Dialog dialog = createAndShowDialog(context, R.layout.action_done_layout);
        dialog.setCanceledOnTouchOutside(true);
        if (finish) dialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
            @Override
            public void onDismiss(DialogInterface dialog) {
                if (context instanceof Activity) ((Activity) context).finish();
            }
        });
        return dialog;
    }

    public static Dialog showDialog(Context context, String message) {
        final Dialog dialog = createAndShowDialog(context, R.layout.default_tip_dialog);
        dialog.setCanceledOnTouchOutside(false);
        XCommon.setText(dialog, R.id.textViewMessage, message);
        dialog.findViewById(R.id.buttonCommit).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                dialog.dismiss();
            }
        });
        return dialog;
    }

    public static Dialog showDefaultInputDialog(final Context context, final String hint, String commit, String cancel, final Callback<String> callback) {
        final Dialog dialog = createAndShowDialog(context, R.layout.default_input_dialog);
        enableInputMethod(dialog);
        dialog.setCanceledOnTouchOutside(false);
        final EditText input = (EditText) dialog.findViewById(R.id.editText);
        View.OnClickListener listener = new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (v.getId() == R.id.buttonCommit) {
                    if (input.length() == 0) {
                        Toast.makeText(context, hint, Toast.LENGTH_SHORT).show();
                    } else {
                        dialog.dismiss();
                        callback.onCallback(input.getText().toString());
                    }
                } else dialog.dismiss();
            }
        };
        input.setHint(hint);
        XCommon.setText(dialog, R.id.buttonCommit, commit).setOnClickListener(listener);
        XCommon.setText(dialog, R.id.buttonCancel, cancel).setOnClickListener(listener);
        return dialog;
    }

    /**
     * @param callback callback commit or not
     */
    public static Dialog showDefaultConfirmDialog(final Context context, final String msg, String commit, String cancel, final Callback<Boolean> callback) {
        final Dialog dialog = createAndShowDialog(context, R.layout.default_confirm_dialog);
        dialog.setCanceledOnTouchOutside(false);
        View.OnClickListener listener = new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                dialog.dismiss();
                callback.onCallback(v.getId() == R.id.buttonCommit);
            }
        };
        XCommon.setText(dialog, R.id.textViewMessage, msg);
        XCommon.setText(dialog, R.id.buttonCommit, commit).setOnClickListener(listener);
        XCommon.setText(dialog, R.id.buttonCancel, cancel).setOnClickListener(listener);
        return dialog;
    }

    public static Dialog showDialog(Context context, @LayoutRes int layoutId) {
        if (context instanceof Activity) {
            if (((Activity) context).isFinishing()) {
                return new Dialog(context);
            }
        }
        final Dialog dialog = createAndShowDialog(context, layoutId);
        dialog.setCanceledOnTouchOutside(false);
        dialog.show();
        return dialog;
    }

    public static void enableInputMethod(Dialog dialog) {
        Window window = dialog.getWindow();
        if (window != null) {
            window.clearFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
            window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
        }
    }

    public static String getFileProviderAuthority(Context context) {
        return context.getPackageName();
    }

    public static void installApk(Context context, File apk) {
        if (apk == null || !apk.exists()) {
            Toast.makeText(context, "File not exists", Toast.LENGTH_LONG).show();
            return;
        }
        try {
            String type = MimeTypeMap.getSingleton().getMimeTypeFromExtension("apk");
            Intent intent = new Intent(Intent.ACTION_VIEW);
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
                Uri contentUri = FileProvider.getUriForFile(context, getFileProviderAuthority(context), apk);
                intent.setDataAndType(contentUri, type);
            } else {
                intent.setDataAndType(Uri.fromFile(apk), type);
            }
            context.startActivity(intent);
        } catch (Exception e) {
            e.printStackTrace();
            Toast.makeText(context, "installApk error", Toast.LENGTH_LONG).show();
        }
    }

    public static Uri getFileUri(Context context, File file) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            return FileProvider.getUriForFile(context, getFileProviderAuthority(context), file);
        } else {
            return Uri.fromFile(file);
        }
    }

    public static Gson getGson() {
        return gson;
    }

    private static Gson gson;

    static {
        GsonBuilder b = new GsonBuilder();
//        BooleanSerializer serializer = new BooleanSerializer();
//        b.registerTypeAdapter(Boolean.class, serializer);
//        b.registerTypeAdapter(boolean.class, serializer);
        gson = b.create();
    }

    private static Map<Context, Dialog> progressDialogs = new HashMap<>();

    private static Dialog getProgressDialog(Context context) {
        Dialog progressDialog = progressDialogs.get(context);
        if (progressDialog == null) {
            progressDialog = createAndShowDialog(context, R.layout.default_progress_dialog);
            progressDialog.setCanceledOnTouchOutside(false);
            progressDialogs.put(context, progressDialog);
        }
        return progressDialog;
    }

    public static Dialog showProgressDialog(Context context) {
        Dialog progressDialog = getProgressDialog(context);
        if (!progressDialog.isShowing()) progressDialog.show();
        return progressDialog;
    }

    public static boolean dismissProgressDialog(Context context) {
        Dialog progressDialog = progressDialogs.get(context);
        if (progressDialog != null && progressDialog.isShowing()) {
            progressDialog.dismiss();
            return true;
        }
        return false;
    }

    public static void onDestroyActivity(Activity activity) {
        progressDialogs.remove(activity);
    }

    private static Dialog createAndShowDialog(Context context, @LayoutRes int layoutId) {
        if (context instanceof Activity) {
            if (((Activity) context).isFinishing()) {
                return new Dialog(context);
            }
        }
        final Dialog dialog = new AlertDialog.Builder(context, R.style.themeDialog).show();
        Window window = dialog.getWindow();
        if (window != null) {
            WindowManager.LayoutParams lp = window.getAttributes();
            lp.width = WindowManager.LayoutParams.MATCH_PARENT;
            window.setAttributes(lp);
        }
        if (layoutId != -1) dialog.setContentView(layoutId);
        dialog.setCanceledOnTouchOutside(false);
        return dialog;
    }

    public static PopupWindow createPopupWindow(Context context, @LayoutRes int layoutId) {
        View contentView = LayoutInflater.from(context).inflate(layoutId, null);
        final PopupWindow popupWindow = new PopupWindow(contentView, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, true);
        popupWindow.setTouchable(true);
        popupWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
        return popupWindow;
    }

    public static PopupWindow createPopupWindow(Context context, @LayoutRes int layoutId, final View.OnClickListener listener) {
        PopupWindow popupWindow = createPopupWindow(context, layoutId);
        getViews((ViewGroup) popupWindow.getContentView(), new Predicate<View>() {
            @Override
            public boolean apply(View view) {
                view.setOnClickListener(listener);
                return true;
            }
        });
        return popupWindow;
    }

    public static PopupWindow createPopMenu(final View view, final IPopMenuAdapter adapter) {
        Context context = view.getContext();
        final PopupWindow popupWindow = Helper.createPopupWindow(context, R.layout.pop_menu);
        ViewGroup contentView = (ViewGroup) popupWindow.getContentView().findViewById(R.id.container);
        contentView.removeAllViews();
        LayoutInflater inflater = LayoutInflater.from(context);
        final int TAG = R.id.container;
        View.OnClickListener listener = new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                popupWindow.dismiss();
                int position = (Integer) v.getTag(TAG);
                adapter.onItemSelected(adapter.getData(position), position);
                ((TextView) view).setText(adapter.getItem(position));
            }
        };
        for (int i = 0; i < adapter.getCount(); i++) {
            TextView textView = (TextView) inflater.inflate(R.layout.pop_item, contentView, false);
            textView.setText(adapter.getItem(i));
            textView.setOnClickListener(listener);
            textView.setTag(TAG, i);
            LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(view.getWidth(), ViewGroup.LayoutParams.WRAP_CONTENT);
            textView.setMinimumHeight(view.getHeight());
            contentView.addView(textView, params);
        }
        return popupWindow;
    }

    public static void initRadioGroupWithStyle(RadioGroup container, int size, @LayoutRes int layoutId) {
        container.removeAllViews();
        LayoutInflater inflater = LayoutInflater.from(container.getContext());
        for (int i = 0; i < size; i++) {
            inflater.inflate(layoutId, container, true);
        }
        container.check(container.getChildAt(0).getId());
    }

    public static void setEnabled(ViewGroup group, boolean enable) {
        for (int i = 0; i < group.getChildCount(); i++) {
            group.getChildAt(i).setEnabled(enable);
        }
    }

    /**
     * @return the index in the root view
     */
    public static int getIndexInRootView(View view, @IdRes int rootViewId) {
        ViewParent viewParent = view.getParent();
        if (viewParent instanceof View) {
            View parent = (View) view.getParent();
            if (parent.getId() != rootViewId) {
                return getIndexInRootView(parent, rootViewId);
            } else {
                return ((ViewGroup) parent).indexOfChild(view);
            }
        } else return -1;
    }

    public static void startCall(Context context, String tel) {
        Intent phoneIntent = new Intent(Intent.ACTION_CALL, Uri.parse("tel:" + tel));
        try {
            context.startActivity(phoneIntent);
        } catch (Exception e) {
            showDialog(context, context.getString(R.string.start_call_permission_tip));
        }
    }

    public static String fromBase64(String base64) {
        return new String(Base64.decode(base64, Base64.DEFAULT));
    }

    public static boolean isNoneEmpty(TextView... textViews) {
        for (TextView textView : textViews) {
            if (textView.length() == 0) {
                Toast.makeText(textView.getContext(), textView.getHint(), Toast.LENGTH_SHORT).show();
                return false;
            }
        }
        return true;
    }

    public static void clearTextView(TextView... textViews) {
        for (TextView textView : textViews) {
            textView.setText("");
        }
    }

    public static void startRunnable(Context context, Runnable action) {
        if (context instanceof Activity) {
            ((Activity) context).runOnUiThread(action);
        } else action.run();
    }


    /**
     * @return start with 0
     */
    public static int getWeek(Calendar calendar) {
        int week = calendar.get(Calendar.DAY_OF_WEEK);
        if (week == Calendar.SUNDAY) {
            week = 7;
        } else week = week - 1;
        return --week;
    }

    private static final String[] weekDays = {"一", "二", "三", "四", "五", "六", "天"};

    /**
     * {"一", "二", "三", "四", "五", "六", "天"};
     */
    public static String getWeekName(Calendar calendar) {
        return weekDays[getWeek(calendar)];
    }

    public static void setVisibility(int visibility, View... views) {
        for (View view : views) {
            view.setVisibility(visibility);
        }
    }

    /**
     * VISIBLE for any VISIBLE kid, GONE for none VISIBLE kids
     */
    public static void setVisibilityDependOnKids(ViewGroup group) {
        int visibility = View.GONE;
        for (int i = 0; i < group.getChildCount(); i++) {
            if (group.getChildAt(i).getVisibility() == View.VISIBLE) {
                visibility = View.VISIBLE;
                break;
            }
        }
        group.setVisibility(visibility);
    }

    /**
     * load common data to new object
     */
    public static void loadData(Object from, Object to, Class<?> commonClass) {
        List<Field> fields = getAllFilds(commonClass);
        for (Field field : fields) {
            try {
                if (!field.isAccessible()) field.setAccessible(true);
                field.set(to, field.get(from));
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
    }

    private static List<Field> getAllFilds(Class<?> cls) {
        List<Field> list = new ArrayList<>();
        while (cls != Object.class) {
            Field[] fields = cls.getDeclaredFields();
            for (Field f : fields) {
                if (!f.getName().startsWith("this")) {
                    list.add(f);
                }
            }
            cls = cls.getSuperclass();
        }
        return list;
    }

    public static void scrollToBottom(final ScrollView scroll) {
        if (scroll == null) return;
        new Handler().post(new Runnable() {
            public void run() {
                int offset = scroll.getChildAt(0).getMeasuredHeight() - scroll.getHeight();
                if (offset < 0) offset = 0;
                scroll.scrollTo(0, offset);
            }
        });
    }
}
